﻿
using Arction.Wpf.SemibindableCharting;
using Arction.Wpf.SemibindableCharting.Axes;
using Arction.Wpf.SemibindableCharting.Series3D;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Threading;

namespace WPF_ExampleSpectrum3D_Demo
{
    public class Model
    {
        public RowDataGenerator rowDataGenerator;

        public Model()
        {
            rowDataGenerator = new RowDataGenerator();
        }

        public AxisX3D GetAxisX3D()
        {
            AxisX3D axisX = new AxisX3D();
            axisX.Orientation = PlaneXAxis3D.XY;
            axisX.CornerAlignment = AxisAlignment3D.Outside;
            axisX.MajorDivTickStyle.Alignment = Alignment.Far;
            axisX.LabelsColor = Color.FromArgb(200, 255, 255, 255);
            axisX.MajorDivTickStyle.Color = Colors.Orange;
            axisX.Title.Text = "Frequency (Hz)";
            axisX.Title.Color = Colors.Yellow;
            return axisX;
        }
        public AxisY3D GetAxisY3D()
        {
            AxisY3D axisY = new AxisY3D();
            axisY.Orientation = PlaneYAxis3D.XY;
            axisY.CornerAlignment = AxisAlignment3D.Outside;
            axisY.MajorDivTickStyle.Alignment = Alignment.Far;
            axisY.LabelsColor = Color.FromArgb(200, 255, 255, 255);
            axisY.MajorDivTickStyle.Color = Colors.Orange;
            axisY.Title.Text = "Power spectrum P(f)";
            axisY.Title.Color = Colors.Yellow;
            return axisY;
        }
        public AxisZ3D GetAxisZ3D()
        {
            AxisZ3D axisZ = new AxisZ3D();
            axisZ.Reversed = true;
            axisZ.LabelsColor = Color.FromArgb(200, 255, 255, 255);
            axisZ.Title.Text = "Time";
            axisZ.Title.Color = Colors.Yellow;
            axisZ.ValueType = AxisValueType.Time;
            axisZ.MajorDivTickStyle.Color = Colors.Orange;
            return axisZ;
        }
        public SurfaceGridSeries3D GetSurfaceGridSeries3D(AxisX3D axisX, AxisZ3D axisZ)
        {
            SurfaceGridSeries3D _surface = new SurfaceGridSeries3D();
            _surface.ContourPalette = CreatePalette(_surface);

            _surface.InitialValue = 10;
            _surface.WireframeType = SurfaceWireframeType3D.None;
            _surface.ContourLineType = ContourLineType3D.None;
            _surface.SetRangesXZ(axisX.Minimum, axisX.Maximum,
                        axisZ.Minimum, axisZ.Maximum);
            _surface.BaseColor = Colors.White;
            return _surface;
        }
        public WaterfallSeries3D GetWaterfallSeries3D1(AxisY3D axisY)
        {
            WaterfallSeries3D m_waterfall = new WaterfallSeries3D();
            m_waterfall.ContourPalette = CreatePalette(m_waterfall);
            m_waterfall.BaseLevel = axisY.Minimum;
            m_waterfall.InitialValue = 0;
            m_waterfall.BaseColor = Colors.White;
            return m_waterfall;
        }
        public WaterfallSeries3D GetWaterfallSeries3D2(AxisY3D axisY)
        {
            WaterfallSeries3D m_waterfallFront = new WaterfallSeries3D();
            m_waterfallFront.ContourPalette = CreatePalette(m_waterfallFront);
            m_waterfallFront.BaseLevel = axisY.Minimum;
            m_waterfallFront.BaseColor = Colors.White;
            return m_waterfallFront;
        }
        public ValueRangePalette CreatePalette(SeriesBase3D ownerSeries)
        {
            ValueRangePalette palette = new ValueRangePalette(ownerSeries);

            palette.Steps.Clear();
            palette.Steps.Add(new PaletteStep(palette, Colors.Blue, 0));
            palette.Steps.Add(new PaletteStep(palette, Colors.Lime, 30));
            palette.Steps.Add(new PaletteStep(palette, Colors.Red, 60));
            palette.Steps.Add(new PaletteStep(palette, Colors.Yellow, 100));
            palette.Type = PaletteType.Gradient;

            return palette;
        }

        public class RowDataGenerator
        {
            public delegate void NewDataGeneratedEventHandler(NewDataGeneratedEventArgs args);

            public event NewDataGeneratedEventHandler NewDataGenerated;
            public event EventHandler Started;
            public event EventHandler Stopped;

            private double[] m_aDataRow;
            private bool m_bRememberPrevData;
            private DispatcherTimer _dispatcherTimer;
            private double m_dInitialValue;
            private double m_dMax;
            private double m_dMin;
            private double m_dVariation;
            private int m_iRowLength;
            private double m_dPeak1X = 0;//peak 1 center 
            private double m_dPeak2X = 0;//peak 2 center 
            private Random _rand = new Random((int)(DateTime.Now.Ticks % int.MaxValue));

            public RowDataGenerator()
            {
                m_aDataRow = null;
                m_bRememberPrevData = true;
                m_dInitialValue = 10.0;
                m_dMax = 100.0;
                m_dMin = 0.0;
                m_dVariation = 10.0;
                m_iRowLength = 10;

                _dispatcherTimer = new DispatcherTimer();
                _dispatcherTimer.Tick += new EventHandler(m_dispatcherTimer_Tick);

            }

            public bool IsRunning
            {
                get
                {
                    return _dispatcherTimer.IsEnabled;
                }
            }

            /// <summary>
            /// Generator output interval in milliseconds.
            /// </summary>
            public int IntervalMs
            {
                get
                {
                    return _dispatcherTimer.Interval.Milliseconds;
                }

                set
                {
                    _dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, value);
                }
            }

            /// <summary>
            /// Remember previous row data. New row data is calculated related to it.
            /// </summary>
            public bool RememberPreviousData
            {
                get
                {
                    return m_bRememberPrevData;
                }

                set
                {
                    m_bRememberPrevData = value;
                }
            }

            /// <summary>
            /// Variation between adjacent points in range 0...100.
            /// </summary>
            public double Variation
            {
                get
                {
                    return m_dVariation;
                }

                set
                {
                    if (value < 0.0)
                    {
                        value = 0.0;
                    }
                    else if (value > 100.0)
                    {

                        value = 100.0;
                    }

                    m_dVariation = value;

                }
            }

            /// <summary>
            /// Frequency stability in range 0...100. Frequency drifting also depends on Variation, which effects especially in low Frequency Stability amounts. 
            /// </summary>
            public double FrequencyStability
            {
                get
                {
                    return m_dFrequencyStability;
                }
                set
                {
                    if (value < 1)
                    {
                        value = 1;
                    }

                    if (value > 100)
                    {
                        value = 100;
                    }

                    m_dFrequencyStability = value;
                }
            }

            private double m_dFrequencyStability = 1;

            /// <summary>
            /// Output value range minimum.
            /// </summary>
            public double MinValue
            {
                get
                {
                    return m_dMin;
                }

                set
                {
                    if (value > m_dMax)
                    {
                        value = m_dMax - 0.1;
                    }

                    m_dMin = value;
                }
            }

            /// <summary>
            /// Output value range maximum.
            /// </summary>
            public double MaxValue
            {
                get
                {
                    return m_dMax;
                }

                set
                {
                    if (value <= m_dMin)
                    {
                        value = m_dMin + 0.1;
                    }

                    m_dMax = value;
                }
            }

            /// <summary>
            /// Created data array length.
            /// </summary>
            public int RowLength
            {
                get
                {
                    return m_iRowLength;
                }

                set
                {
                    if (value < 1)
                    {
                        value = 1;
                    }

                    m_iRowLength = value;
                    m_aDataRow = null;

                    m_dPeak1X = m_iRowLength / 8.0;
                    m_dPeak2X = m_iRowLength / 2.0;
                }
            }

            /// <summary>
            /// Initial value of the first row.
            /// </summary>
            public double InitialValue
            {
                get
                {
                    return m_dInitialValue;
                }

                set
                {
                    m_dInitialValue = value;
                }
            }

            /// <summary>
            /// Start the generator.
            /// </summary>
            public void Start()
            {
                if (_dispatcherTimer.IsEnabled == false)
                {
                    m_dPeak1X = m_iRowLength / 8.0;
                    m_dPeak2X = m_iRowLength / 2.0;
                    if (_dispatcherTimer.Interval == null)
                    {
                        _dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 25);
                    }

                    _dispatcherTimer.Start();

                    if (Started != null)
                    {
                        Started(this, null);
                    }
                }

            }
            private void Timer_Tick(object sender, EventArgs e)
            {
                GenerateData();
            }

            // <summary>
            // Stop the generator.
            //</summary>
            public void Stop()
            {
                if (_dispatcherTimer.IsEnabled == true)
                {
                    _dispatcherTimer.Stop();

                    if (Stopped != null)
                    {
                        Stopped(this, null);
                    }
                }
            }


            /// <summary>
            /// Generate data.
            /// </summary>
            public double[] GenerateData()
            {
                double[] aNewData = new double[m_iRowLength];

                for (int i = 0; i < m_iRowLength; i++)
                {
                    aNewData[i] = m_dInitialValue;
                }

                if (m_bRememberPrevData && m_aDataRow != null)
                {
                    for (int i = 0; i < m_iRowLength; i++)
                    {
                        aNewData[i] = m_aDataRow[i];
                    }
                }

                //Calc new data
                double dAmplitude = m_dVariation / 100.0 * (m_dMax - m_dMin);
                Random rand = _rand;

                double dHalf = m_iRowLength / 2.0;
                m_dPeak1X += (rand.NextDouble() - 0.5) * m_dVariation / m_dFrequencyStability / 100.0 * m_iRowLength / 2.0;
                m_dPeak2X += (rand.NextDouble() - 0.5) * m_dVariation / m_dFrequencyStability / 100.0 * m_iRowLength;
                if (m_dPeak1X < 0)
                {
                    m_dPeak1X = 0;
                }

                if (m_dPeak1X > m_iRowLength)
                {
                    m_dPeak1X = m_iRowLength;
                }

                if (m_dPeak2X < 0)
                {
                    m_dPeak2X = 0;
                }

                if (m_dPeak2X > m_iRowLength)
                {
                    m_dPeak2X = m_iRowLength;
                }

                double dNewValue1, dNewValue2, dX1, dX2;
                double dPeakY1 = m_dMax / 3.0 * 2.0;
                double dPeakY2 = m_dMax / 2.0;
                double dNarrowFactor1 = 8.0;
                double dNarrowFactor2 = 24.0;

                double dA1 = dPeakY1 / (dHalf * dHalf) * dNarrowFactor1;
                double dA2 = dPeakY2 / (dHalf * dHalf) * dNarrowFactor2;
                double dSum12;
                for (int i = 0; i < m_iRowLength; i++)
                {
                    dX1 = i - m_dPeak1X;
                    dX2 = i - m_dPeak2X;
                    dNewValue1 = dPeakY1 - dX1 * dX1 * dA1;
                    if (dNewValue1 < m_dMin)
                    {
                        dNewValue1 = m_dMin;
                    }

                    if (dNewValue1 > m_dMax)
                    {
                        dNewValue1 = m_dMax;
                    }

                    dNewValue2 = dPeakY2 - dX2 * dX2 * dA2;
                    if (dNewValue2 < m_dMin)
                    {
                        dNewValue2 = m_dMin;
                    }

                    if (dNewValue2 > m_dMax)
                    {
                        dNewValue2 = m_dMax;
                    }

                    dSum12 = dNewValue1 + dNewValue2;
                    dSum12 = dSum12 + dSum12 * (rand.NextDouble() - 0.5) * m_dVariation / 10.0;

                    aNewData[i] = (aNewData[i] + dSum12) / 2.0;
                    if (aNewData[i] < m_dMin)
                    {
                        aNewData[i] = m_dMin;
                    }

                    if (aNewData[i] > m_dMax)
                    {
                        aNewData[i] = m_dMax;
                    }
                }


                if (NewDataGenerated != null)
                {
                    NewDataGenerated(new NewDataGeneratedEventArgs(aNewData));
                }

                if (m_aDataRow == null || m_aDataRow.Length != m_iRowLength)
                {
                    m_aDataRow = new double[m_iRowLength];
                }

                Array.Copy(aNewData, m_aDataRow, aNewData.Length);

                return aNewData;
            }

            private void m_dispatcherTimer_Tick(object sender, EventArgs e)
            {
                GenerateData();
            }

            public void StartButton()
            {
                Start();
            }

            public void StopButton()
            {
                Stop();
            }

            public void VariationTextChanged(int iVariation)
            {
                m_dVariation = iVariation;
            }

            public void IntervalTextChanged(int iIntervalInMs)
            {
                _dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, iIntervalInMs);
            }
        }

        public class NewDataGeneratedEventArgs
        {
            public double[] Data;

            public NewDataGeneratedEventArgs(double[] data)
            {
                Data = data;
            }
        }

    }

}
